/**
 * @class 轨迹绘制器
 * @author DuskyHuang 圣子
 * @description 提供天体轨迹线等相关的渲染和逻辑。
*/

import { Color, Mat4, Vec3, _decorator } from 'cc';
import { Mediator, core, utils } from 'cfw';
import { AxisRotation, I8, SceneGraph } from 'const';
import { Dsm, PlanetarySystem, SolarSystem } from 'prefabs';
import { GizmosProxy, planetary, cosmos, gizmos, solar, visit } from 'proxies';
import { CosmosController } from '../controllers/cosmos-controller';
import { attenuate } from 'algorithms';
const { ccclass } = _decorator;

@ccclass('cocosmos.TracksRenderer')
export class TracksRenderer extends Mediator {

    protected onLoad(): void { this._init()._listen(); }

    protected start(): void { }

    protected lateUpdate(dt?: time.sec): void {
        if (!gizmos.showTracks) return;
        const { solar: inSolar, helio, planetary: inPlanetary } = cosmos;
        const { showPlanets, showDwarfs, showMinors, showComets } = solar;
        const { showSatellites } = planetary;
        const { inStellar } = visit;
        if (inPlanetary && showSatellites) this._drawSatellitesTracks();
        if (inSolar && showPlanets) this._drawPlanetsTracks();
        if (inSolar && showDwarfs) this._drawDwarfsTracks();
        if (inSolar && showMinors) this._drawMinorsTracks();
        if (inSolar && showComets) this._drawCometsTracks();
        if (helio && !inStellar) this._drawHelioTracks();
    }

    private _drawSatellitesTracks(): void {
        const rdr = this._csc.mainRenderer;
        const { satellites, node: { worldMatrix } } = this._planetSys;
        const { center } = visit;
        if (!center) return;
        for (const satellite of satellites) {
            if (!satellite.node.activeInHierarchy) continue;
            const { orbitMat, orbitalRadius } = satellite;
            color.set(SATELLITE);
            const central = satellite === visit.center;
            // const distSqr = Vec3.squaredDistance(satellite.node.worldPosition, center.node.worldPosition);
            const att = central ? 1 : attenuate(satellite.distSqr, TrackAtt, true);
            if (att <= 0.01) continue;
            color.a *= att;
            rdr.addCircle(Vec3.ZERO, orbitalRadius, color, (central ? 5 : 1) * CIRCLE, true, true, Mat4.multiply(mat4, worldMatrix, orbitMat));
        }
    }

    private _drawPlanetsTracks(): void {
        const rdr = this._csc.mainRenderer;
        const { planets, node: { worldMatrix } } = this._solarSys;
        const { inSatellite: satellite } = visit;
        for (const planet of planets) {
            if (!planet.node.activeInHierarchy) continue;
            const { orbitMat, orbitalRadius, orbitAngle } = planet;
            const central = planet === visit.center;
            color.set(PLANET);
            const visiting = central || satellite && (visit.center as Dsm).belong === planet;
            if (visiting) {
                Mat4.multiply(mat4, worldMatrix, orbitMat);
                const arc = Math.clamp(0.01, 1, 1e-3 / planet.node.worldScale.y);
                rdr.addTrackCurve(Vec3.ZERO, orbitalRadius, color, CIRCLE, true, true, mat4, orbitAngle, true, arc);
                rdr.addTrackCurve(Vec3.ZERO, orbitalRadius, color, CIRCLE, true, true, mat4, orbitAngle, false, arc);
            } else {
                const att = attenuate(planet.distSqr, TrackAtt, true);
                if (att <= 0.01) continue;
                color.a *= att;
                rdr.addCircle(Vec3.ZERO, orbitalRadius, color, CIRCLE, true, true, Mat4.multiply(mat4, worldMatrix, orbitMat));
            }
        }
    }

    private _drawDwarfsTracks(): void {
        const rdr = this._csc.mainRenderer;
        const { sun, dwarfs, node: { worldMatrix } } = this._solarSys;
        // const center = sun.node.worldPosition;
        for (const dwarf of dwarfs) {
            if (!dwarf.node.activeInHierarchy) continue;
            const { orbitMat, orbitAngle, orbitalRadius, orbitalVelocity } = dwarf;
            const central = dwarf === visit.center;
            color.set(DWARF);
            const att = central ? 1 : attenuate(dwarf.distSqr, TrackAtt, true);
            if (att <= 0.01) continue;
            color.a *= att;
            const anticlockwise = orbitalVelocity > 0;
            // rdr.addCircle(center, orbitalRadius, color, CIRCLE, true, true, Mat4.multiply(mat4, worldMatrix, orbitMat));
            rdr.addTrackCurve(Vec3.ZERO, orbitalRadius, color, CIRCLE,
                true, anticlockwise, Mat4.multiply(mat4, worldMatrix, orbitMat), orbitAngle, true, 1.0);
        }
    }

    private _drawMinorsTracks(): void {
        const rdr = this._csc.mainRenderer;
        const { sun, minors, node: { worldMatrix } } = this._solarSys;
        // const center = sun.node.worldPosition;
        for (const minor of minors) {
            if (!minor.node.activeInHierarchy) continue;
            const { orbitMat, orbitalRadius, orbitAngle, orbitalVelocity } = minor;
            const central = minor === visit.center;
            color.set(MINOR);
            // const distSqr = Vec3.squaredDistance(minor.node.worldPosition, center);
            const att = central ? 1 : attenuate(minor.distSqr, TrackAtt, true);
            if (att <= 0.01) continue;
            color.a *= att;
            Mat4.multiply(mat4, worldMatrix, orbitMat);
            const anticlockwise = orbitalVelocity > 0;
            rdr.addTrackCurve(Vec3.ZERO, orbitalRadius, color, CIRCLE, true, anticlockwise, mat4, orbitAngle, true, 0.8);
        }
    }

    private _drawCometsTracks(): void {
        const rdr = this._csc.mainRenderer;
        const { sun, comets, node: { worldMatrix } } = this._solarSys;
        const center = sun.node.worldPosition;
        for (const comet of comets) {
            if (!comet.node.activeInHierarchy) continue;
            const { orbitMat, orbitalRadius, orbitAngle, orbitalVelocity } = comet;
            color.set(COMET);
            // const distSqr = Vec3.squaredDistance(comet.node.worldPosition, center);
            const att = visit.center === comet ? 1 : attenuate(comet.distSqr, TrackAtt, true);
            if (att <= 0.01) continue;
            color.a *= att;
            Mat4.multiply(mat4, worldMatrix, orbitMat);
            const anticlockwise = orbitalVelocity > 0;
            rdr.addTrackCurve(Vec3.ZERO, orbitalRadius, color, CIRCLE, false, anticlockwise, mat4, orbitAngle, true, 0.6);
        }
    }

    private _drawHelioTracks(): void {
        const rdr = this._csc.mainRenderer, { pow10 } = cosmos;
        color.set(_SOLAR);
        color.a *= attenuate(pow10, SOLAR_TRACK_ATT, false);
        rdr.addFadeLine(Vec3.ZERO, v3a.set(SOLAR_DIR).multiplyScalar(SOLAR_TRACK_LENGTH), color, LINE, true);
        rdr.addFadeLine(Vec3.ZERO, v3a.negative(), color, LINE, true);
    }

    private _init(): this {
        this._csc = core.getController(CosmosController);
        this._solarSys = utils.findComp(SolarSystem, COSMOS, SOLAR);
        this._planetSys = utils.findComp(PlanetarySystem, COSMOS, PLANETARY);
        return this;
    }

    private _listen(): void { }
    private _csc: CosmosController;
    private _solarSys: SolarSystem;
    private _planetSys: PlanetarySystem;
}

const { COSMOS, SOLAR, PLANETARY } = SceneGraph;
const { TrackColors: { SATELLITE, PLANET, DWARF, MINOR, COMET, SOLAR: _SOLAR }, TrackAtt, Segments: { CIRCLE, LINE } } = GizmosProxy;
const { SOLAR_DIR } = AxisRotation;
const SOLAR_TRACK_ATT: attenuation = [-2.5, -2, -0.5, 0];
const SOLAR_TRACK_LENGTH = 3;
const v3a = new Vec3;
const mat4 = new Mat4;
const color = new Color;